grammar CSS3;

options 
{
	output=AST ;
	language=CSharp3 ;
	k=4 ;
}

tokens {
	IMPORT;
	NESTED;
	NEST;
	RULE;
	ATTRIB;
	PARENTOF;
	PRECEDEDS;
	ATTRIBEQUAL;
	HASVALUE;
	HASVALUE2;
	BEGINSWITH;
	ENDSWITH;
	ATTRCONTAINS;
	PSEUDO;
	PROPERTY;
	FUNCTION;
	TAG;
	ID;
	CLASS;
}

@header {
using System;
using System.Text;
using System.Globalization;
using System.Collections.Generic;

using KLF.Lib.Browser.DOM.CSS;
using KLF.Lib.Browser.DOM.CSS.SAC;
using KLF.Lib.Browser.DOM.CSS.SAC.Conditions;
using KLF.Lib.Browser.DOM.CSS.SAC.Selectors;
}

@namespace
{ KLF.Lib.Browser.DOM.CSS.SAC }

@lexer::members
{
    private IToken last;
}

@parser::members
{
	/* nothing here yet */
}

stylesheet returns [CSSStyleSheet value]
@init {
	ssheet = new CSSStyleSheet();
}
	: importRule* ( nested | nr=ruleset { ssheet.AddRule(nr.value); } )+ { $value = ssheet; }
	;

importRule
	: ('@import' | '@include')  STRING -> ^( IMPORT STRING )
	;

nested
 	: '@' nest '{' properties? nested* '}' -> ^( NESTED nest properties* nested* )
	;

nest
	: IDENT IDENT* pseudo* -> ^( NEST IDENT IDENT* pseudo* )
	;
	
ruleset returns [CSSRule value]
@init {
	newRule = new CSSStyleDeclaration();
}
 	: s1=selectors { newRule.Selectors=s1; } '{' properties? '}' -> ^( RULE selectors properties* ) { $value = newRule; }
	;
	
selectors returns [SelectorList value]
@init {
	selectorList = new SelectorList();
}
	: s1=selector { selectorList.Add(s1); } (',' s2=selector { selectorList.Add(s2); } )* { $value = selectorList; }
	;
	
selector returns [Selector value]
@init {
	builder = new SelectorBuilder();
}
	: e1=elem { builder.EStack.Add(e1); } ( so1=selectorOperation { builder.EStack.Add(so1); } )*   attrib* pseudo? ->  elem selectorOperation* attrib* pseudo* { $value = builder.getSelector(); }
	;

selectorOperation
	: selectop? elem -> selectop* elem
	;

selectop
	: '>' -> PARENTOF
        | '+'  -> PRECEDEDS
	;

properties
	: declaration (';' declaration?)* ->  declaration+
	;
	
elem
	:     IDENT -> ^( TAG IDENT )
	| '#' IDENT -> ^( ID IDENT )
	| '.' IDENT -> ^( CLASS IDENT )
	;

pseudo
	: (':'|'::') IDENT -> ^( PSEUDO IDENT )
	| (':'|'::') function -> ^( PSEUDO function )
	;

attrib
	: '[' IDENT (attribRelate (STRING | IDENT))? ']' -> ^( ATTRIB IDENT (attribRelate STRING* IDENT*)? )
	;
	
attribRelate
	: '='  -> ATTRIBEQUAL
	| '~=' -> HASVALUE
	| '|=' -> HASVALUE2
	| '^=' -> BEGINSWITH
	| '$=' -> ENDSWITH
	| '*=' -> ATTRCONTAINS
	;	
  
declaration
	: IDENT ':' args -> ^( PROPERTY IDENT args )
	;

args
	: expr (','? expr)* -> expr*
	;

expr
	: (NUM unit?)
	| IDENT
	| COLOR
	| STRING
	| function
	;

unit
	: ('%'|'px'|'cm'|'mm'|'in'|'pt'|'pc'|'em'|'ex'|'deg'|'rad'|'grad'|'ms'|'s'|'hz'|'khz')
	;
	
function
	: IDENT '(' args? ')' -> IDENT '(' args* ')'
	;

IDENT
	:	('_' | 'a'..'z'| 'A'..'Z' | '\u0100'..'\ufffe' ) 
		('_' | '-' | 'a'..'z'| 'A'..'Z' | '\u0100'..'\ufffe' | '0'..'9')*
	|	'-' ('_' | 'a'..'z'| 'A'..'Z' | '\u0100'..'\ufffe' ) 
		('_' | '-' | 'a'..'z'| 'A'..'Z' | '\u0100'..'\ufffe' | '0'..'9')*
	;

// string literals
STRING
	:	'"'! (~('"'|'\n'|'\r'))* '"'!
	|	'\''! (~('\''|'\n'|'\r'))* '\''!
	;

NUM
	:	'-' (('0'..'9')* '.')? ('0'..'9')+
	|	(('0'..'9')* '.')? ('0'..'9')+
	;

COLOR
	:	'#' ('0'..'9'|'a'..'f'|'A'..'F')+
	;

// Single-line comments
SL_COMMENT
	:	'//'
		(~('\n'|'\r'))* ('\n'|'\r'('\n')?)
		{$channel=HIDDEN;}
	;
	
// multiple-line comments
COMMENT
	:	'/*' .* '*/' { $channel = HIDDEN; }
	;

// Whitespace -- ignored
WS	: ( ' ' | '\t' | '\r' | '\n' | '\f' )+ { $channel = HIDDEN; }
	;

